﻿using AirConditionControl.Controls;
using AirConditionControl.Models;
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TextBox;

namespace AirConditionControl
{
    public partial class MainForm : Form
    {
        private SerialPort serialPort = null;
        private IModbusMaster master = null;
        private System.Timers.Timer timer = null;
        private List<SlaveInfo> slaves = new List<SlaveInfo>();
        private List<XFloor> xFloors = new List<XFloor>();
        private List<ParamInfo> paramInfos = new List<ParamInfo>();
        private List<XFan> xFans = new List<XFan>();

        private Dictionary<string, bool> stateMap = new Dictionary<string, bool>();
        private Dictionary<string, ushort> dataMap = new Dictionary<string, ushort>();

        int floorNo = 0;
        ushort startAddress = 0;
        ushort fanCount = 0;
        bool isCollecting = false;  // 采集状态
        bool allStartFlag = true;  // 是否一次性启动

        public MainForm()
        {
            InitializeComponent();
            InitControlState();
            InitData();
            // 初始哈定时器
            timer = new System.Timers.Timer();
            timer.AutoReset = true;
            timer.Interval = 1000;
            timer.Elapsed += Timer_Elapsed;
        }

        /// <summary>
        /// 初始化界面控件状态
        /// </summary>
        private void InitControlState()
        {
            allCtrlBtn.Enabled = false;
        }

        /// <summary>
        /// 链接串口设备
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            if (serialPort == null)
            {
                serialPort = new SerialPort();
                serialPort.PortName = "COM2";
                serialPort.DataBits = 8;
                serialPort.Parity = Parity.None;
                serialPort.StopBits = StopBits.One;
                serialPort.BaudRate = 9600;
            }

            if (serialPort.IsOpen)
            {
                serialPort.Close();
                connBtn.BackColor = Color.LawnGreen;
                connBtn.Text = "建立链接";

                connStateLb.ForeColor = Color.Orange;
                connStateLb.Text = "未建立链接";
            }
            else
            {
                try
                {
                    serialPort.Open();
                    master = ModbusSerialMaster.CreateRtu(serialPort);

                    allCtrlBtn.Enabled = true;
                    connBtn.BackColor = Color.Orange;
                    connBtn.Text = "断开连接";

                    connStateLb.ForeColor = Color.LightGreen;
                    connStateLb.Text = "已建立链接";
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "链接建立失败", MessageBoxButtons.OK);
                }
            }
        }

        /// <summary>
        /// 给所有风机控件定义状态转换事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void uAirControl_ChangeStateEvent(object sender, EventArgs e)
        {
            if (sender is UAirControl)
            {
                UAirControl? control = sender as UAirControl;
                control.State = !control.State;

                string stateParamName = control.StateParamName;
                ParamInfo? paramInfo = paramInfos.Find(p => p.ParamName == stateParamName);
                byte slaveId = paramInfo.SlaveId;
                ushort address = paramInfo.Address;

                try
                {
                    master.WriteSingleCoil(slaveId, address, control.State);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK);
                }
            }
        }

        /// <summary>
        /// 配置文件数据初始化
        /// </summary>
        private void InitData()
        {
            string rootPath = "ConfigFiles";
            string slaveCfgPath = $"{rootPath}/Slave.xml";
            string xFansCfgPath = $"{rootPath}/XFans.xml";
            string xFloorsCfgPath = $"{rootPath}/XFloors.xml";
            string xParamCfgPath = $"{rootPath}/XParams.xml";

            LoadConfigData(slaveCfgPath, LoadSlaveInfo);
            LoadConfigData(xParamCfgPath, LoadParamInfo);
            LoadConfigData(xFloorsCfgPath, LoadFloors);
            LoadConfigData(xFansCfgPath, LoaXFans);


            // 加载楼层信息
            LoadLayer();
            // 加载风机信息
            LoadFansByFloor();
        }

        private void LoadLayer()
        {
            List<XFloor> cbList = new List<XFloor>();
            cbList.Add(new XFloor()
            {
                FloorNo = 0,
                FloorName = "全部楼层",
                RunFirstAddress = 0,
                TemperFirstAddress = 0,
                FanCount = 0
            });
            cbList.AddRange(xFloors);

            layerCbox.Items.Clear();
            layerCbox.DisplayMember = "FloorName";
            layerCbox.ValueMember = "FloorNo";
            layerCbox.DataSource = cbList;
        }

        private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            ReadAndLoad();
        }

        /// <summary>
        /// 读取和加载数据
        /// </summary>
        private void ReadAndLoad()
        {
            Task.Run(() =>
            {
                // 读取
                ReadFromDevice();
                // 加载
                LoadData();
            });
        }

        private void ReadFromDevice()
        {
            stateMap.Clear();
            dataMap.Clear();

            foreach (SlaveInfo slave in slaves)
            {
                byte slaveId = slave.SlaveId;
                ushort address = slave.StartAddress;
                int funCode = slave.FunctionCode;
                ushort count = slave.Count;

                List<ParamInfo> paramInfos1 = paramInfos.FindAll(p => p.SlaveId == slaveId);

                if (funCode == 1)
                {
                    try
                    {
                        bool[] flags = master.ReadCoils(slaveId, address, count);
                        foreach (ParamInfo param in paramInfos1)
                        {
                            stateMap.Add(param.ParamName, flags[param.Address]);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message,"提示",MessageBoxButtons.OK,MessageBoxIcon.Error);
                    }
                }
                else if (funCode == 3)
                {
                    try
                    {
                        ushort[] datas = master.ReadHoldingRegisters(slaveId, address, count);

                        foreach (ParamInfo param in paramInfos1)
                        {
                            dataMap.Add(param.ParamName, datas[param.Address]);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void LoadData()
        {
            Control.ControlCollection controls = flowPanel.Controls;
            foreach (Control control in controls)
            {
                UAirControl airControl = control as UAirControl;

                bool f1 = stateMap.TryGetValue(airControl.StateParamName, out bool state);
                if (f1)
                {
                    this.Invoke(() =>
                    {
                        airControl.State = state;
                    });
                }

                bool f2 = dataMap.TryGetValue(airControl.TemperParamName, out ushort temper);
                if (f2)
                {
                    float val = Convert.ToInt32(temper) / 10f;
                    this.Invoke(() =>
                    {
                        airControl.Temperature = $"{val}℃";
                    });
                }
            }
        }

        private void LoadConfigData(string filePath, Action<XmlElement> action)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(filePath);
            XmlElement? parent = doc.DocumentElement;
            if (parent == null) return;

            action.Invoke(parent);
        }
        private void LoadSlaveInfo(XmlElement root)
        {
            XmlNodeList nodeList = root.ChildNodes;

            foreach (XmlNode node in nodeList)
            {
                string? slaveId = node.SelectSingleNode("SlaveId").InnerText;
                string? funCode = node.SelectSingleNode("FunctionCode").InnerText;
                string? startAddress = node.SelectSingleNode("StartAddress").InnerText;
                string? count = node.SelectSingleNode("Count").InnerText;
                slaves.Add(new SlaveInfo()
                {
                    SlaveId = Convert.ToByte(slaveId),
                    FunctionCode = Convert.ToInt32(funCode),
                    StartAddress = Convert.ToUInt16(startAddress),
                    Count = Convert.ToUInt16(count)
                }); ;
            }
        }

        private void LoadParamInfo(XmlElement root)
        {
            XmlNodeList nodeList = root.ChildNodes;

            foreach (XmlNode node in nodeList)
            {
                string? paramName = node.SelectSingleNode("ParamName").InnerText;
                string? slaveId = node.SelectSingleNode("SlaveId").InnerText;
                string? address = node.SelectSingleNode("Address").InnerText;
                string? dataType = node.SelectSingleNode("DataType").InnerText;
                string? note = node.SelectSingleNode("Note").InnerText;

                paramInfos.Add(new ParamInfo()
                {
                    ParamName = paramName,
                    SlaveId = Convert.ToByte(slaveId),
                    Address = Convert.ToUInt16(address),
                    DataType = dataType,
                    Note = note
                });
            }
        }

        private void LoadFloors(XmlElement root)
        {
            XmlNodeList nodeList = root.ChildNodes;

            foreach (XmlNode node in nodeList)
            {
                string? floorNo = node.SelectSingleNode("FloorNo").InnerText;
                string? floorName = node.SelectSingleNode("FloorName").InnerText;
                string? runFirstAddress = node.SelectSingleNode("RunFirstAddress").InnerText;
                string? temperFirstAddress = node.SelectSingleNode("TemperFirstAddress").InnerText;
                string? fanCount = node.SelectSingleNode("FanCount").InnerText;

                xFloors.Add(new XFloor()
                {
                    FloorNo = Convert.ToInt32(floorNo),
                    FloorName = floorName,
                    RunFirstAddress = Convert.ToByte(runFirstAddress),
                    TemperFirstAddress = Convert.ToByte(temperFirstAddress),
                    FanCount = Convert.ToUInt16(fanCount)
                });
            }
        }

        private void LoaXFans(XmlElement root)
        {
            XmlNodeList nodeList = root.ChildNodes;

            foreach (XmlNode node in nodeList)
            {
                string? fanName = node.SelectSingleNode("FanName").InnerText;
                string? stateParamName = node.SelectSingleNode("StateParamName").InnerText;
                string? temperParamName = node.SelectSingleNode("TemperParamName").InnerText;

                xFans.Add(new XFan()
                {
                    FanName = fanName,
                    StateParamName = stateParamName,
                    TemperParamName = temperParamName
                });
            }
        }

        private void LoadFansByFloor()
        {
            flowPanel.Controls.Clear();
            List<XFan> loadedFans = new List<XFan>();
            floorNo = Convert.ToInt32(layerCbox.SelectedValue);

            if (floorNo > 0)
            {
                // 加载某楼层的风机
                loadedFans = xFans.Where(f => f.FanName.StartsWith($"{floorNo}-")).ToList();
            }
            else
            {
                // 加载所有楼层的风机
                loadedFans = xFans;
            }

            // 开始加载风机
            int i = 0;
            foreach (XFan fan in loadedFans)
            {
                UAirControl airControl = new UAirControl();
                airControl.Size = new Size(300, 240);
                airControl.Name = $"fan-{i}";
                airControl.DeviceName = fan.FanName;
                airControl.StateParamName = fan.StateParamName;
                airControl.TemperParamName = fan.TemperParamName;
                airControl.ChangeStateEvent += uAirControl_ChangeStateEvent;

                flowPanel.Controls.Add(airControl);
                i++;
            }
        }

        private void layerCbox_SelectedIndexChanged(object sender, EventArgs e)
        {
            GetReadRange();
            LoadFansByFloor();
        }

        /// <summary>
        /// 获取当前操作的对象范围
        /// </summary>
        private void GetReadRange()
        {
            floorNo = Convert.ToInt32(layerCbox.SelectedValue);
            if (floorNo > 0)
            {
                XFloor? floor = xFloors.Find(f => f.FloorNo == floorNo);
                startAddress = floor.RunFirstAddress;
                fanCount = floor.FanCount;
            }
            else
            {
                startAddress = 0;
                fanCount = Convert.ToUInt16(xFloors.Sum(f => f.FanCount));
            }
        }

        /// <summary>
        /// 一键启动关闭
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CollectionControl(object sender, EventArgs e)
        {
            if (!isCollecting)
            {
                MessageBox.Show("还未开始采集数据，无法启停设备");
                return;
            }
            GetReadRange();
            if (allStartFlag)
            {
                allStartFlag = false;
                OpenDevice(false);
                allCtrlBtn.Text = "一键启动";
            }
            else
            {
                allStartFlag = true;
                OpenDevice(true);
                allCtrlBtn.Text = "一键关闭";
            }
        }

        private void OpenDevice(bool flag)
        {
            try
            {
                List<bool> flags = new List<bool>();
                int i = 0;
                while (i < fanCount)
                {
                    flags.Add(flag);
                    i++;
                };
                master.WriteMultipleCoils(1, startAddress, flags.ToArray());
                string msg = flag ? "一键开启成功" : "一键关闭成功";
                MessageBox.Show(msg, "提示");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "提示");
            }
        }

        private void beginCollect(object sender, EventArgs e)
        {
            if (isCollecting)
            {
                isCollecting = false;
                collectBtn.BackColor = Color.Blue;
                collectBtn.Text = "开始采集";
                timer.Stop();
            }
            else
            {
                isCollecting = true;
                collectBtn.BackColor = Color.Purple;
                collectBtn.Text = "停止采集";
                timer.Start();
            }
        }
    }
}
